//
// This code was created by Jeff Molofee '99 (ported to Linux/GLUT by Richard Campbell '99)
//
// If you've found this code useful, please let me know.
//
// Visit me at www.demonews.com/hosted/nehe 
// (email Richard Campbell at ulmont@bellsouth.net)
//
#include <GL/glut.h>    // Header File For The GLUT Library 
#include <GL/gl.h>	// Header File For The OpenGL32 Library
#include <GL/glu.h>	// Header File For The GLu32 Library
#include <GL/glx.h>
#include <unistd.h>     // Header file for sleeping.
#include <string.h>	// Header File For strlen
#include <math.h>
#include <stdio.h>

/* ascii code for the escape key */
#define ESCAPE 27

/* The number of our GLUT window */
int window; 

GLuint		base = 0;
GLfloat		rot = 0.0f;
GLuint		texture[1];//纹理对象

struct Image {
	unsigned long sizeX;
	unsigned long sizeY;
	char* data;
};
typedef struct Image Image;

int ImageLoad(char* filename, Image* image)
{
	FILE* file;
	unsigned long size;
	unsigned long i;
	unsigned short int planes;
	unsigned short int bpp;
	char temp;

	if ((file = fopen(filename, "r")) == NULL)
	{
		printf("File Not Found: %s\n", filename);
		return 0;
	}

	fseek(file, 18, SEEK_CUR);

	if((i = fread(&image->sizeX, 4, 1, file)) != 1)
	{
		printf("Error reading width from %s.\n", filename);
		return 0;
	}
	printf("Width of %s: %lu\n", filename, image->sizeX);

	if((i = fread(&image->sizeY, 4, 1, file)) != 1)
	{
		printf("Error reading height from %s.\n", filename);
		return 0;
	}
	printf("Height of %s: %lu\n", filename, image->sizeY);

	size = image->sizeX * image->sizeY * 3;

	if ((i = fread(&planes, 2, 1, file)) != 1)
	{
		printf("Planes from %s is not 1: %u\n", filename, planes);
		return 0;
	}

	if ((i = fread(&bpp, 2, 1, file)) != 1)
	{
		printf("Error reading bpp from %s.\n", filename);
		return 0;
	}

	if(bpp != 24)
	{
		printf("Bpp from %s is not 24: %u\n", filename, bpp);
		return 0;
	}

	fseek(file, 24, SEEK_CUR);

	image->data = (char*)malloc(size);
	if(image->data == NULL)
	{
		printf("Error allocation memory for color-corrected image data");
		return 0;
	}

	if((i = fread(image->data, size, 1, file)) != 1)
	{
		printf("Error readint image data from %s.\n", filename);
		return 0;
	}

	for (i = 0; i < size; i += 3)
	{
		temp = image->data[i];
		image->data[i] = image->data[i+2];
		image->data[i+2] = temp;
	}

	return 1;
}


void LoadGLTextures()
{
	Image* image1;

	image1 = (Image*) malloc(sizeof(Image));
	if(image1 == NULL)
	{
		printf("Error allocating space for image.");
		exit(0);
	}

	if (!ImageLoad("Data/lesson15/LIGHTS.BMP", image1))
	{
		exit(1);
	}

	glGenTextures(1, &texture[0]);					// Create The Texture

	glBindTexture(GL_TEXTURE_2D, texture[0]);
	gluBuild2DMipmaps(GL_TEXTURE_2D, 3, image1->sizeX, image1->sizeY, GL_RGB, GL_UNSIGNED_BYTE, image1->data);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
	glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
	glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
	glEnable(GL_TEXTURE_GEN_S);
	glEnable(GL_TEXTURE_GEN_T);

	if (image1)									// If Texture Exists
	{
		if (image1->data)							// If Texture Image Exists
		{
			free(image1->data);					// Free The Texture Image Memory
		}

		free(image1);								// Free The Image Structure
	}

}


GLvoid BuildFont(GLvoid)
{
	Display *dpy;
	XFontStruct *fontInfo;//存储字体 

	base = glGenLists(256);//用来存储256个字符

	//加载字体。 你所拥有的字体的种类是依赖你的系统，在我的系统上，字体信息在/etc/X11/fonts文件夹下
	//得到当前显示器。这个操作在显示器环境中打开了第二个到显示器的连接，它将持续足够长来加载字体
	dpy = XOpenDisplay(NULL);//默认的显示器环境

	int font_count, i;
	char **fonts;
	printf("All gb2312 fonts on this server:\n");
	fonts = XListFonts(dpy, "*ZapfDingbats*", 10000, &font_count);
	for(i=0; i<font_count; i++)
		printf("%s\n", fonts[i]);
	XFreeFontNames(fonts);

	/*
	 * An Xwindows font name looks like this:
	 * -foundry-                     // Replaced with a wild card: *
	 * family-                       // times-, courier-, etc
	 * weight-                       // medium-, bold-
	 * slant-                        // r-, i- (italic), o- (oblique)
	 * setWidth--                    // Always normal--
	 * pixels-                       // Wildcarded because of resolution independence: *-
	 * height_decipoints-            // 100-, 120-, 140-, 180-, 240-
	 * horizontalResolution_dpi-     // 75- or 100-
	 * verticalResolution_dpi-       // 75- or 100-
	 * spacing-                      // m- for Courier, else p-
	 * averageWidth_decipixel-       // Wildcarded: *-
	 * characterSet                  // iso8859-1 or adobe-fontspecific
	 */

	fontInfo = XLoadQueryFont(dpy, "-adobe-itc zapf dingbats-medium-r-normal--*-*-*-*-*-*-adobe-fontspecific");
	if (fontInfo == NULL)
	{
		printf("没有找到指定的字体\n");
		fontInfo = XLoadQueryFont(dpy, "fixed");
		if (fontInfo == NULL)
		{
			printf("No X font available?\n");
		}
	}

	//加载字体信息以后，现在是最好的时机来旋转、绽放或是冷却字体
	//以32号字体（空格）开始，共256个字符，将它们存在以base的起始处
	glXUseXFont(fontInfo->fid, 0,256, base);

	//释放我们得到的字体，因为我们已经得到显示列表
	XFreeFont(dpy, fontInfo);

	//关闭第二个连接
	XCloseDisplay(dpy);
}

//删除字体
GLvoid KillFont(GLvoid) 
{
	glDeleteLists(base, 256);//删除所有的256个字符
}

GLvoid glPrint(char *text)//传统的gl输入方式
{
	if (text == NULL)//如果没有内容要输出，什么也不做
	{
		return;
	}

	glPushAttrib(GL_LIST_BIT);//// 把显示列表属性压入属性堆栈
	glListBase(base);//将初始字符设为0
	glCallLists(strlen(text), GL_UNSIGNED_BYTE, text);//绘制显示列表文字
	glPopAttrib();//恢复GL_LIST_BIT属性

}

/* A general OpenGL initialization function.  Sets all of the initial parameters. */
void InitGL(int Width, int Height)	        // We call this right after our OpenGL window is created.
{
	LoadGLTextures();
	BuildFont();//加载字体

	glClearColor(0.0f, 0.0f, 0.0f, 0.5f);		// This Will Clear The Background Color To Black
	glClearDepth(1.0f);				// Enables Clearing Of The Depth Buffer
	glEnable(GL_DEPTH_TEST);			// Enables Depth Testing
	glDepthFunc(GL_LEQUAL);				// The Type Of Depth Test To Do
	glShadeModel(GL_SMOOTH);			// Enables Smooth Color Shading

	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);// 告诉系统对透视进行修正
	glEnable(GL_LIGHT0);								// Enable Default Light (Quick And Dirty)
	glEnable(GL_LIGHTING);								// Enable Lighting
	glEnable(GL_COLOR_MATERIAL);						// Enable Coloring Of Material

	glEnable(GL_TEXTURE_2D);					// 使用二维纹理
	glBindTexture(GL_TEXTURE_2D, texture[0]);			// 选择使用的纹理

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();				// Reset The Projection Matrix

	gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f);	// Calculate The Aspect Ratio Of The Window

	glMatrixMode(GL_MODELVIEW);
}

/* The function called when our window is resized (which shouldn't happen, because we're fullscreen) */
void ReSizeGLScene(int Width, int Height)
{
	if (Height==0)				// Prevent A Divide By Zero If The Window Is Too Small
		Height=1;

	glViewport(0, 0, Width, Height);		// Reset The Current Viewport And Perspective Transformation

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

	gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

/* The main drawing function. */
void DrawGLScene()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear The Screen And The Depth Buffer
	glLoadIdentity();									// Reset The View
	glTranslatef(1.1f*((float)(cos(rot/16.0f))),0.8f*((float)(sin(rot/20.0f))),-30.0f);
	glRotatef(rot,1.0f,0.0f,0.0f);						// Rotate On The X Axis
	glRotatef(rot*1.2f,0.0f,1.0f,0.0f);					// Rotate On The Y Axis
	glRotatef(rot*1.4f,0.0f,0.0f,1.0f);					// Rotate On The Z Axis
	glTranslatef(-0.35f,-0.35f,0.1f);					// Center On X, Y, Z Axis

	glPrint("N");										// Draw A Skull And Crossbones Symbol
	rot+=0.1f;											// Increase The Rotation Variable

	// since this is double buffered, swap the buffers to display what just got drawn.
	glutSwapBuffers();
}

/* The function called whenever a key is pressed. */
void keyPressed(unsigned char key, int x, int y) 
{
	/* avoid thrashing this procedure */
	usleep(100);

	/* If escape is pressed, kill everything. */
	if (key == ESCAPE) 
	{ 
		/* shut down our window */
		glutDestroyWindow(window); 

		KillFont();//销毁字体

		/* exit the program...normal termination. */
		exit(0);                   
	}
}

int main(int argc, char **argv) 
{  
	fprintf(stdout, "注意：此程序不能得到正确的结果，因为缺乏能输出图案的字体。");
	/* Initialize GLUT state - glut will take any command line arguments that pertain to it or 
	   X Windows - look at its documentation at http://reality.sgi.com/mjk/spec3/spec3.html */  
	glutInit(&argc, argv);  

	/* Select type of Display mode:   
	   Double buffer 
	   RGBA color
	   Alpha components supported 
	   Depth buffer */  
	glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH);  

	/* get a 640 x 480 window */
	glutInitWindowSize(640, 480);  

	/* the window starts at the upper left corner of the screen */
	glutInitWindowPosition(0, 0);  

	/* Open a window */  
	window = glutCreateWindow("Jeff Molofee's GL Code Tutorial ... NeHe '99");  

	/* Register the function to do all our OpenGL drawing. */
	glutDisplayFunc(&DrawGLScene);  

	/* Go fullscreen.  This is as soon as possible. */
	glutFullScreen();

	/* Even if there are no events, redraw our gl scene. */
	glutIdleFunc(&DrawGLScene);

	/* Register the function called when our window is resized. */
	glutReshapeFunc(&ReSizeGLScene);

	/* Register the function called when the keyboard is pressed. */
	glutKeyboardFunc(&keyPressed);

	/* Initialize our window. */
	InitGL(640, 480);

	/* Start Event Processing Engine */  
	glutMainLoop();  

	return 1;
}

